#include "mainwindow.h"
#include "ui_mainwindow.h"

//#define OUTPUT_TO_FILE
//#define AUDIO_RECORDER
#define THREAD_OUTPUT

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    readConfig();
    initDialog();
    initToolButton();
    initMessageBox();
    initMenuBar();
    initAudioInput();
    initSystemTrayIcon();
    slot_disconnected();
#ifdef THREAD_OUTPUT
    initThread();
#else
    initUDP();
#endif
    ui->hSliderVolume->setRange(0, 100);
    ui->hSliderVolume->setValue(100);

    ui->page_2->setText("等待对方接听中...");
}

MainWindow::~MainWindow()
{
    on_toolBtnHangUp_clicked();
#ifdef THREAD_OUTPUT
    m_writeThread->quit();
    m_writeThread->wait();
#endif
    delete ui;
}

void MainWindow::handleStateChanged(QAudio::State state)
{
    switch (state) {
    case QAudio::StoppedState:
        if (m_audioInput->error() != QAudio::NoError) {
            // Error handling
            qDebug() << m_audioInput->error();
        } else {
            // Finished recording
        }
        break;

    case QAudio::ActiveState:
        // Started recording - read from IO device
        break;

    default:
        // ... other cases as appropriate
        break;
    }
}

void MainWindow::slot_callRequest()
{
    if (m_messageBox.exec() == -1)
        return;

    QMessageBox::StandardButton button = m_messageBox.standardButton(m_messageBox.clickedButton());
    if (button == QMessageBox::Yes) {
        slot_connected();
        m_socket.writeDatagram(m_protocolManager.protocals().value(Protocol::Accept).toLocal8Bit(),
                               QHostAddress(m_targetIP), m_targetPort);
        m_inputDevice = m_audioInput->start();
        connect(m_inputDevice, &QIODevice::readyRead, this, &MainWindow::slot_sendAudioData, Qt::UniqueConnection);
    } else if (button == QMessageBox::No) {
        m_socket.writeDatagram(m_protocolManager.protocals().value(Protocol::Refuse).toLocal8Bit(),
                               QHostAddress(m_targetIP), m_targetPort);
    }
}

void MainWindow::slot_callResponse(int response)
{
//    ui->textEdit->append("Response");
    ui->stackedWidget->setCurrentIndex(0);
    m_dialogTimer.stop();
    if (response == 0) {
        slot_connected();
        m_inputDevice = m_audioInput->start();
        connect(m_inputDevice, &QIODevice::readyRead, this, &MainWindow::slot_sendAudioData, Qt::UniqueConnection);
    } else if (response == 1) {
        // TODO 添加拒绝通话时，将等待框关掉
    }

}

void MainWindow::slot_sendAudioData()
{
    m_socket.writeDatagram(m_inputDevice->read(1024), QHostAddress(m_targetIP), m_targetPort);
}

void MainWindow::slot_callCancel()
{
    m_messageBox.button(QMessageBox::No)->click();
}

void MainWindow::slot_manualCancel()
{
    ui->stackedWidget->setCurrentIndex(0);
    m_dialogTimer.stop();
    int size = m_socket.writeDatagram(m_protocolManager.protocals().value(Protocol::Cancel).toLocal8Bit(),
                                      QHostAddress(m_targetIP), m_targetPort);
    qDebug() << "取消拨打，发送字节数: " << size << "ip,port: " << m_targetIP << "," << m_targetPort;
}

void MainWindow::initAudioInput()
{
    // Set up the desired format, for example:
    m_format.setSampleRate(8000);
    m_format.setChannelCount(1);
    m_format.setSampleSize(8);
    m_format.setCodec("audio/pcm");
    m_format.setByteOrder(QAudioFormat::LittleEndian);
    m_format.setSampleType(QAudioFormat::SignedInt);
    QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
    if (!info.isFormatSupported(m_format)) {
        qWarning() << "Default format not supported, trying to use the nearest.";
        m_format = info.nearestFormat(m_format);
    }

    m_audioInput = new QAudioInput(m_format);
//    connect(m_audioInput, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));

    m_audioOutput = new QAudioOutput(m_format, this);
    m_outputDevice = m_audioOutput->start();
}

void MainWindow::initUDP()
{
    bool ret = m_socket.bind(hostIP(), m_hostPort);
    QString text = ret ? "绑定成功 " : "绑定失败";
    connect(&m_socket, &QUdpSocket::readyRead, this, &MainWindow::slot_reciveAudioDate);
}

void MainWindow::initThread()
{
    m_recvData = new RecvData(m_outputDevice);
    m_writeThread = new QThread;
    connect(m_writeThread, &QThread::started, m_recvData, &RecvData::slot_start);
    connect(this, &MainWindow::signal_hangUp, m_recvData, &RecvData::slot_hangUp);
    connect(m_recvData, &RecvData::signal_hangUp, this, [=] () {
        m_audioInput->stop();
        slot_disconnected();
    }, Qt::QueuedConnection);
    connect(m_recvData, &RecvData::signal_callRequest, this, &MainWindow::slot_callRequest, Qt::QueuedConnection);
    connect(m_recvData, &RecvData::signal_callResponse, this, &MainWindow::slot_callResponse, Qt::QueuedConnection);
    connect(m_recvData, &RecvData::signal_callCancel, this, &MainWindow::slot_callCancel, Qt::QueuedConnection);
    connect(ui->page_2, &CallWaiting::signal_manualCancel, this, &MainWindow::slot_manualCancel);
    m_recvData->moveToThread(m_writeThread);
    m_writeThread->start();
}

void MainWindow::addSettingItem(const QString itemName,
                                QStringList settingOption,
                                void (MainWindow::*function)(QAction *))
{
    QAction *firstAction = new QAction(itemName, m_settingMenu);
    QMenu *firstMenu = new QMenu(ui->menubar);
    QActionGroup *firstActionGroup = new QActionGroup(ui->menubar);
    for (int i = 0; i < settingOption.size(); i++) {
        QAction *action = new QAction(settingOption[i], this);
        action->installEventFilter(this);
        action->setCheckable(true);
        firstActionGroup->addAction(action);
        firstMenu->addAction(action);

        if (i == 0)
            action->setChecked(true);
    }
    connect(firstActionGroup, &QActionGroup::triggered, this, function);
    firstAction->setMenu(firstMenu);
    m_settingMenu->addAction(firstAction);
}

void MainWindow::initMenuBar()
{
    m_settingAction = new QAction("设置", this);
    m_settingMenu = new QMenu(this);
    auto info = QAudioDeviceInfo::defaultInputDevice();
    {
        QStringList ratesToString;
        foreach (int rate, info.supportedSampleRates()){
            ratesToString.push_back(QString::number(rate));
        }
        addSettingItem("采样率", ratesToString, &MainWindow::slot_setRates);
    }
    {
        QStringList channelToString;
        foreach (int channel, info.supportedChannelCounts()){
          channelToString.push_back(QString::number(channel));
        }
        addSettingItem("通道数", channelToString, &MainWindow::slot_setChannel);
    }
    {
      QStringList sizesToString;
      foreach (int size, info.supportedSampleSizes()){
        sizesToString.push_back(QString::number(size));
      }
      addSettingItem("采样位数", sizesToString, &MainWindow::slot_setSize);
    }
    addSettingItem("编码方式", info.supportedCodecs(), &MainWindow::slot_setCodec);
    {
        m_endien.insert(0, "BigEndian");
        m_endien.insert(1, "LittleEndian");
        QStringList ordersToString;
        foreach (int order, info.supportedByteOrders()){
          ordersToString.push_back(m_endien.value(order));
        }
        addSettingItem("字节序", ordersToString, &MainWindow::slot_setByteOrder);
    }
    {
      m_sampleType.insert(0, "Unknown");
      m_sampleType.insert(1, "SignedInt");
      m_sampleType.insert(2, "UnSignedInt");
      m_sampleType.insert(3, "Float");
      QStringList typesToString;
      foreach (int type, info.supportedSampleTypes()){
        typesToString.push_back(m_sampleType.value(type));
      }
      addSettingItem("采样类型", typesToString, &MainWindow::slot_setType);
    }

    m_settingAction->setMenu(m_settingMenu);
    ui->menubar->addAction(m_settingAction);
}

void MainWindow::initSystemTrayIcon()
{
    {
        QAction *actionOpen = new QAction("打开软件界面", &m_trayMenu);
        connect(actionOpen, &QAction::triggered, this, &MainWindow::show);
        m_trayMenu.addAction(actionOpen);
    }

    {
        QAction *actionHangup = new QAction("挂断", &m_trayMenu);
        connect(actionHangup, &QAction::triggered, this, &MainWindow::on_toolBtnHangUp_clicked);
        m_trayMenu.addAction(actionHangup);
    }

    {
        QAction *actionQuit = new QAction("退出", &m_trayMenu);
        connect(actionQuit, &QAction::triggered, this, &MainWindow::close);
        m_trayMenu.addAction(actionQuit);
    }

    m_tray.setToolTip("语音通话");
    m_tray.setContextMenu(&m_trayMenu);
    m_tray.setIcon(QIcon("./images/iconDisconnected.png"));
    connect(&m_tray, &QSystemTrayIcon::activated, qApp, &QApplication::quit);
    m_tray.show();
}

QHostAddress MainWindow::hostIP()
{
    auto list = QNetworkInterface::allInterfaces();
    foreach (QNetworkInterface interface, list) {
        if (interface.type() != QNetworkInterface::Ethernet)
            continue;

        if (interface.humanReadableName().contains("VMware"))
            continue;

        foreach (auto entry ,interface.addressEntries()) {
            if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol)
                return entry.ip();
        }
    }
    return QHostAddress();
}

void MainWindow::setSettingsDisabled(bool disable)
{
    foreach (auto action, m_settingMenu->findChildren<QAction*>()) {
        action->setDisabled(disable);
    }
}

void MainWindow::slot_reciveAudioDate()
{
    QNetworkDatagram datagram = m_socket.receiveDatagram();
    m_outputDevice->write(datagram.data());
}

void MainWindow::readConfig()
{
    QString dir = QApplication::applicationDirPath();
    QSettings settings(dir+"/config.ini", QSettings::IniFormat);

    m_targetIP = settings.value("Network/targetIP").toString();
    m_targetPort = settings.value("Network/targetPort").toInt();
}

void MainWindow::initToolButton()
{
    ui->toolBtnCall->setIcon(QIcon(":images/images/call.png"));
    ui->toolBtnCall->setIconSize(QSize(48, 48));
    ui->toolBtnCall->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    ui->toolBtnCall->setText("拨通");

    ui->toolBtnHangUp->setIcon(QIcon(":images/images/hangUp.png"));
    ui->toolBtnHangUp->setIconSize(QSize(48, 48));
    ui->toolBtnHangUp->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
    ui->toolBtnHangUp->setText("挂断");
}

void MainWindow::initMessageBox()
{
    m_messageBox.setIcon(QMessageBox::Information);
    m_messageBox.setText("您有一个来电，是否接听？");
    m_messageBox.setWindowTitle("通话");
    m_messageBox.addButton(QMessageBox::Yes);
    m_messageBox.addButton(QMessageBox::No);
}

void MainWindow::initDialog()
{
    connect(&m_dialogTimer, &QTimer::timeout, this, [this] () {
        ui->stackedWidget->setCurrentIndex(1);
        on_toolBtnHangUp_clicked();
        slot_manualCancel();
        QMessageBox::information(this, "通话", "对方无人接听!");
    });
}

void MainWindow::slot_setRates(QAction *action)
{
    m_format.setSampleRate(action->text().toInt());
    delete m_audioInput;
    m_audioInput = new QAudioInput(m_format);
}

void MainWindow::slot_setSize(QAction *action)
{
    m_format.setSampleSize(action->text().toInt());
    delete m_audioInput;
    m_audioInput = new QAudioInput(m_format);
}

void MainWindow::slot_setChannel(QAction *action)
{
    m_format.setChannelCount(action->text().toInt());
    delete m_audioInput;
    m_audioInput = new QAudioInput(m_format);
}

void MainWindow::slot_setCodec(QAction *action)
{
    m_format.setCodec(action->text());
    delete m_audioInput;
    m_audioInput = new QAudioInput(m_format);
}

void MainWindow::slot_setByteOrder(QAction *action)
{
    m_format.setByteOrder((QAudioFormat::Endian)m_endien.key(action->text()));
    delete m_audioInput;
    m_audioInput = new QAudioInput(m_format);
}

void MainWindow::slot_setType(QAction *action)
{
    m_format.setSampleType((QAudioFormat::SampleType)m_sampleType.key(action->text()));
    delete m_audioInput;
    m_audioInput = new QAudioInput(m_format);
}

void MainWindow::on_toolBtnCall_clicked()
{
    int size = m_socket.writeDatagram(m_protocolManager.protocals().value(Protocol::CallRequest).toLocal8Bit(),
                                      QHostAddress(m_targetIP), m_targetPort);
    qDebug() << "拨打电话，发送字节数: " << size << "ip,port: " << m_targetIP << "," << m_targetPort;
    if (size <= 0) {
        QAbstractSocket::SocketError error = m_socket.error();
        qCritical() << "拨打电话失败，错误: " << error;
        if (error == QAbstractSocket::NetworkError)
            QMessageBox::information(this, "通话", "网络连接异常!");
        return;
    }
    m_dialogTimer.start(30000);
    m_callWaiting.setText("等待对方接通，请稍后。");
    ui->stackedWidget->setCurrentIndex(1);
}


void MainWindow::on_toolBtnHangUp_clicked()
{
    slot_disconnected();
    m_audioInput->stop();
    int size = m_socket.writeDatagram(m_protocolManager.protocals().value(Protocol::HangUp).toLocal8Bit(),
                                      QHostAddress(m_targetIP), m_targetPort);
    qDebug() << "挂断电话，发送字节数: " << size << "ip,port: " << m_targetIP << "," << m_targetPort;
    Q_EMIT signal_hangUp();
}

void MainWindow::slot_connected()
{
    m_tray.setIcon(QIcon(":images/images/icon.png"));
    setSettingsDisabled(true);
    ui->timerWidget->show();
    ui->timerWidget->start();
    ui->toolBtnHangUp->show();
    ui->toolBtnCall->hide();
}

void MainWindow::slot_disconnected()
{
    m_tray.setIcon(QIcon(":images/images/iconDisconnected.png"));
    setSettingsDisabled(false);
    ui->timerWidget->hide();
    ui->timerWidget->stop();
    ui->toolBtnHangUp->hide();
    ui->toolBtnCall->show();
}

void MainWindow::on_hSliderVolume_valueChanged(int value)
{
    ui->labelVolume->setText(QString::number(value));
    qreal volume = (qreal)value / 100;
    m_audioOutput->setVolume(volume);
}

void MainWindow::slot_systemTrayActived(QSystemTrayIcon::ActivationReason reason)
{
    if (reason == QSystemTrayIcon::DoubleClick) {
        show();
    }
}

